from rlinterface import InterfaceElement, InputHandler
from rlcore import *
from rlkbinput import *
import math

from rltext import *


from erlglobals import *

# These will never be layouted BECAUSE we're using SDL which cannot be resized
# in a way that will affect the number of tiles on the screen.

def nullResponseHandler( response ):
	pass

def descriptorPercent( value, span ):
	mn, mx = span
	rv = (100*(value - mn)) / float( mx - mn )
	return "%0.2lf%%" % rv
		

class DialogInputHandler (InputHandler):
	def __init__(self, interface, dialog):
		InputHandler.__init__( self, interface )
		self.keys = {}
		self.dialog = dialog
	def handle(self, event):
		self.dialog.handle( event )
		
class CenteredDialog (InterfaceElement):
	def __init__(self, width, height, *args, **kwargs):
		try:
			kwargs["priority"] = max(1, kwargs["priority"])
		except KeyError:
			kwargs["priority"] = 1
		InterfaceElement.__init__( self, *args, **kwargs )
		self.handler = DialogInputHandler( self.interface, self )
		self.recenter( width, height )
		self.resize( width, height )
	def recenter(self, width, height ):
		self.move( (self.interface.width - width)/ 2, (self.interface.height-height)/2 )
	def quit(self):
		self.handler.quit()
		del self.interface.elements[ self.name ]
	def drawBorder(self, fg, bg):
		glyph = Glyph( ' ', fg, bg )
		for y0 in (0,self.height-1):
			self.interface.move( self.xLayout + 1, self.yLayout + y0 )
			self.interface.putString( "-" * (self.width-2), glyph )
		for y in range(self.yLayout, self.yLayout+self.height):
			if y == self.yLayout or (y+1) == (self.yLayout+self.height):
				glyph.char = '+'
			else:
				glyph.char = '|'
			for x0 in (0,self.width-1):
				self.interface.move( self.xLayout + x0, y )
				self.interface.put( glyph )
	def drawBackground(self, bg):
		background = Glyph( ' ', (0,0,0), bg )
		for y in range(self.yLayout + 1, self.yLayout + self.height - 1 ):
			self.interface.move( self.xLayout + 1, y )
			for i in range(self.width - 2):
				self.interface.put( background )
	def handle(self, event):
		self.quit()
	def draw(self):
		self.drawBorder( stdBorderForeground, stdBorderBackground )
		self.drawBackground( stdBackground )
	def drawLines(self, lines, y):
		for length, line in lines:
			contentWidth = self.width - 2
			offset = (contentWidth-length)/2
			self.interface.move( self.xLayout + 1 + offset, y )
			self.interface.putFormattedText( line, limit=contentWidth, defaultForeground=stdForeground, defaultBackground=stdBackground )
			y += 1
		return y

def WrapForDialog( text, width ):
	from rltext import TextWrapper
	wrapper = TextWrapper( width = width, pageHeight = almostInfinity, moreIndicator = None )
	pages = wrapper.parse( text )
	assert len(pages) == 1
	rv = []
	for line in pages[0]:
		rv.append( (wrapper.textLength(line), line) )
	return rv

class TextDisplay (CenteredDialog):
	def __init__(self, text, width, onDismiss = None, *args, **kwargs):
		kwargs[ "width" ] = width
		self.contentWidth = width - 2
		self.lines = WrapForDialog( text, self.contentWidth )
		kwargs[ "height" ] = 2 + len(self.lines)
		self.onDismiss = onDismiss
		CenteredDialog.__init__(self, *args, **kwargs)
	def draw(self):
		self.drawBorder( stdBorderForeground, stdBorderBackground )
		self.drawBackground( stdBackground )
		self.drawLines( self.lines, self.yLayout + 1 )
	def handle(self, event):
		key = KeyboardInterpreter( event )
		if key.isAscii( " " ):
			self.quit()
			if self.onDismiss:
				self.onDismiss()



class StringQuery (CenteredDialog):
	def __init__(self, query, width, allowed = None, maximalLength = almostInfinity, onAnswer = nullResponseHandler, inputColour = (255,255,255), inputBgColour = (0,0,0), allowEmpty = False, *args, **kwargs):
		self.lines = WrapForDialog( query, width - 2 )
		kwargs["width"] = width
		kwargs["height"] = len(self.lines) + 2 + 2
		CenteredDialog.__init__(self, *args, **kwargs)
		self.inputColour = inputColour
		self.inputBgColour = inputBgColour
		self.answer = ""
		self.contentLength = self.width - 2 - 1 
		self.inputLength = min( self.contentLength, maximalLength )
		self.onAnswer = onAnswer
		self.allowEmpty = allowEmpty
		import string
		self.allowed = allowed
		if not self.allowed:
			self.allowed = string.ascii_letters + " " + string.punctuation
	def handle(self, event):
		key = KeyboardInterpreter( event )
		text = key.asText( allowed = self.allowed )
		if key.isEnter():
			if self.allowEmpty or len(self.answer) > 0:
				self.quit()
				self.onAnswer( self.answer.strip() )
		elif key.isBackspace():
			self.answer = self.answer[:-1]
		elif text:
			if self.inputLength == 1:
				self.answer = text
			else:
				if len( self.answer ) < self.inputLength:
					if text.isspace():
						self.answer = self.answer.strip()
					self.answer += text
				self.answer = self.answer.lstrip()
	def draw(self):
		self.drawBorder( stdBorderForeground, stdBorderBackground )
		self.drawBackground( stdBackground )
		y = self.yLayout + 1
		for length, line in self.lines:
			offset = (self.contentLength-length)/2
			self.interface.move( self.xLayout + 1 + offset, y )
			self.interface.putFormattedText( line, defaultForeground=stdForeground, defaultBackground=stdBackground )
			y += 1
		y += 1
		protoglyph = Glyph( ' ', self.inputColour, self.inputBgColour )
		if self.contentLength == self.inputLength:
			linedata = self.answer + "_"
			if len(linedata) < (self.width-2):
				linedata += " " * max(0,(self.contentLength+1-len(linedata)))
			self.interface.move( self.xLayout + 1, y )
			self.interface.putString( linedata, protoglyph )
		else:
			linedata = self.answer
			if self.inputLength > 1:
				linedata += "_"
			#xOffset = self.xLayout + 1 + (self.contentLength-self.inputLength)//2 
			xOffset = self.xLayout + 1 + (self.contentLength-len(linedata))//2 
			self.interface.move( xOffset , y )
			self.interface.putString( " " * self.inputLength, protoglyph )
			self.interface.move( xOffset, y )
			self.interface.putString( linedata, protoglyph )


class InventoryMenu (CenteredDialog):
	def __init__(self, inventory, width, height, query, itemFilter = None, onAnswer = nullResponseHandler, *args, **kwargs):
		kwargs[ "width" ] = width
		kwargs[ "height" ] = height
		CenteredDialog.__init__(self, *args, **kwargs)
		self.inventory = inventory
		assert not self.inventory or max( self.inventory.items.keys() ) < len(SlotNames)
		if query:
			self.lines = WrapForDialog( query, width - 2 )
		else:
			self.lines = []
		self.itemsPerPage = height - 2 - len(self.lines)
		self.page = 0
		items = 0
		for item in self.inventory:
			if not itemFilter or itemFilter( item ): items += 1
		self.numberOfPages = int( math.ceil( items / self.itemsPerPage ) )
		self.itemKeys = sorted( self.inventory.items.keys() )
		self.onAnswer = onAnswer
		self.itemFilter = itemFilter
	def draw(self):
		self.drawBackground( stdBackground )
		self.drawBorder( stdBorderForeground, stdBorderBackground )
		y = self.yLayout + 1 
		y = self.drawLines( self.lines, y )
		if self.lines: y += 1
		for i in range(self.itemsPerPage):
			if i >= len(self.inventory): continue
			key = self.itemKeys[i]
			if self.itemFilter and not self.itemFilter( self.inventory.items[key] ): continue
			slotName = SlotNames[ key ]
			lineWidth = self.width - 2
			text = "[<RED>%s</RED>] %s" % (slotName, self.inventory.items[key].description())
			wrapper = TextWrapper( width= almostInfinity, pageHeight=almostInfinity, moreIndicator = None)
			pages = wrapper.parse( text )
			text = pages[0][0]
			self.interface.move( self.xLayout + 1, y )
			self.interface.putFormattedText( text, limit = lineWidth, defaultForeground=stdForeground, defaultBackground=stdBackground )
			y += 1
	def handle(self, event):
		key = KeyboardInterpreter( event )
		text = key.asText()
		if text == "+":
			self.page = min( self.page + 1, self.numberOfPages - 1 )
		elif text == "-":
			self.page = max( self.page - 1, 0 )
		elif text == " ":
			self.quit()
			self.onAnswer( None )
		if text:
			try:
				i = SlotNames.index( text )
				item = self.inventory.items[i]
				if not self.itemFilter or self.itemFilter( item ):
					self.quit()
					self.onAnswer( item )
			except ValueError: #non-slot key
				pass
			except KeyError: #non-used key
				pass

class AmountChoice (CenteredDialog):
	def __init__(self, query, width, selection, minimum, maximum, descriptor, onAnswer = nullResponseHandler, *args, **kwargs ):
		if query:
			self.noquery = False
			self.lines = WrapForDialog( query, width - 2 )
			kwargs[ "height" ] = len(self.lines) + 2 + 2
		else:
			self.noquery = True
			kwargs[ "height" ] = 2 + 1
		kwargs[ "width" ] = width
		CenteredDialog.__init__(self, *args, **kwargs)
		self.selection = selection
		self.descriptor = descriptor
		self.range = self.minimum, self.maximum = minimum, maximum
		self.contentWidth = width - 2
		self.positive = (64,200,64)
		self.negative = (200,64,64)
		stepN = 10
		self.bigStep = int( (self.maximum - self.minimum) / float(stepN) + 0.5 )
		self.bigStep = max( self.bigStep, 1 )
		self.onAnswer = onAnswer
	def handle(self, event):
		key = KeyboardInterpreter( event )
		v = key.asCardinalVector()
		delta = None
		if not v: v = key.asViVector()
		if not v: v = key.asUppercaseViVector()
		if not v: v = key.asNumpadVector()
		if key.isEnter():
			self.quit()
			self.onAnswer( self.selection )
		elif v and v[1] == 0:
			delta = v[0]
		elif key.isAscii( "-" ): delta = -1 
		elif key.isAscii( "+" ): delta = 1 
		if delta:
			if key.isShifted():
				delta *= self.bigStep
			self.selection += delta
			self.selection = min( self.selection, self.maximum )
			self.selection = max( self.selection, self.minimum )
	def draw(self):
		self.drawBorder( stdBorderForeground, stdBorderBackground )
		self.drawBackground( stdBackground )
		y = self.yLayout + 1
		if not self.noquery:
			for length, line in self.lines:
				offset = (self.contentWidth-length)/2
				self.interface.move( self.xLayout + 1 + offset, y )
				self.interface.putFormattedText( line, defaultForeground=stdForeground, defaultBackground=stdBackground )
				y += 1
			y += 1
		text = self.descriptor( self.selection, self.range )
		textOffset = (self.contentWidth - len(text))/2
		text = " " * textOffset + text + " " * (self.contentWidth-len(text)-textOffset)
		ratio = (self.selection - self.minimum) / float( self.maximum - self.minimum )
		redOffset = int( ( ratio * self.contentWidth) + 0.5 )
		self.interface.move( self.xLayout + 1, y )
		colour = self.positive
		for i in range(self.contentWidth):
			if i >= redOffset: colour = self.negative
			self.interface.put( Glyph( text[i], (255,255,255), colour ) )

class OptionChoice (CenteredDialog):
	def __init__(self, query, width, descriptions, values, height = None, onAnswer = nullResponseHandler, selection = 0, *args, **kwargs):
		self.descriptions = descriptions
		self.values = values
		assert len( self.descriptions ) == len( self.values )
		assert len( self.values ) > 0
		maxLen = max( [len(opt) for opt in self.descriptions] ) + 8 # +8 for indicators
		if query:
			query += "<N>"
			self.lines = WrapForDialog( query, width - 2 )
			for length, line in self.lines:
				if length > maxLen: maxLen = length
		else:
			self.lines = [ ]
		width = kwargs["width"] = maxLen + 2 + 2
		if not height:
			kwargs["height"] = len(self.lines) + 2 + len(self.descriptions)
		else:
			kwargs["height"] = height
		maxDialogHeight = screenHeight - 2
		if kwargs["height"] > maxDialogHeight:
			kwargs["height"] = maxDialogHeight
		CenteredDialog.__init__(self, *args, **kwargs)
		self.selection = selection
		#self.options = options
		self.contentWidth = width - 2
		self.onAnswer = onAnswer
		queryHeight = len(self.lines)
		self.itemsPerPage = self.height - 2 - queryHeight
		self.numberOfPages = int( math.ceil( len(self.descriptions) / float( self.itemsPerPage ) ) )
		self.page = self.selection // self.itemsPerPage
	def handle(self, event):
		key = KeyboardInterpreter( event )
		v = key.asCardinalVector()
		if not v: v = key.asViVector()
		if not v: v = key.asNumpadVector()
		text = key.asText()
		if text:
			try:
				idx = SlotNames.index( text )
				if idx < self.itemsPerPage and (self.page * self.itemsPerPage + idx) < len(self.descriptions):
					ridx = self.page * self.itemsPerPage + idx
					self.quit()
					self.onAnswer( self.values[ ridx ] )
					return
			except ValueError:
				pass
		if key.isEnter():
			self.quit()
			self.onAnswer( self.values[self.selection] )
		elif v and v[0] == 0:
			self.selection += v[1]
			self.selection = min( self.selection, len(self.values) - 1 )
			self.selection = max( self.selection, 0 )
			self.page = self.selection // self.itemsPerPage
		elif key.isAscii( "+" ) and (self.page+1) < self.numberOfPages:
			self.page += 1
			self.selection = self.page * self.itemsPerPage
		elif key.isAscii( "-" ) and self.page > 0:
			self.page -= 1
			self.selection = (self.page+1) * self.itemsPerPage - 1
	def draw(self):
		self.drawBorder( stdBorderForeground, stdBorderBackground )
		self.drawBackground( stdBackground )
		y = self.yLayout + 1
		for length, line in self.lines:
			offset = (self.contentWidth-length)/2
			self.interface.move( self.xLayout + 1 + offset, y )
			self.interface.putFormattedText( line, defaultForeground=stdForeground, defaultBackground=stdBackground )
			y += 1
		for j in range(self.itemsPerPage):
			i = self.page * self.itemsPerPage + j
			slotName = SlotNames[j]
			if i >= len(self.descriptions): continue
			fg,bg = stdForeground, stdBackground
			option = self.descriptions[i]
			if self.selection == i:
				option = "<DARKBRASS>==></DARKBRASS> [<DARKBRASS>%s</DARKBRASS>] %s" % (slotName, option )
			else:
				option = "<INVISIBLE>==></INVISIBLE> [<DARKBRASS>%s</DARKBRASS>] %s" % (slotName, option )
			#protoglyph = Glyph( ' ', fg, bg )
			lineWidth = self.width - 2
			wrapper = TextWrapper( width= almostInfinity, pageHeight=almostInfinity, moreIndicator = None)
			pages = wrapper.parse( option )
			text = pages[0][0]
			self.interface.move( self.xLayout + 1, y )
			self.interface.putFormattedText( text, limit = lineWidth, defaultForeground=stdForeground, defaultBackground=stdBackground )
			y += 1

class MenuSystem:
	def __init__(self, interface):
		self.interface = interface
	def choice(self, choices, values, query = None, onAnswer=None, name = None):
		if not name: name = "designer-menu-%s" % randomString() # honestly I no longer remember why I require a unique ID for this
		if not onAnswer: onAnswer = self.callOnAnswer
		OptionChoice( interface=self.interface, query=query, width=60, descriptions=choices, values=values, onAnswer=onAnswer, name = name )
	def enterInteger(self, onAnswer, query = None, name = None):
		if not name: name = "designer-menu-%s" % randomString() # honestly I no longer remember why I require a unique ID for this
		StringQuery( allowed="0123456789", interface=self.interface, query=query, width=60, onAnswer=onAnswer, name = name )
	def enterGlyph(self, onAnswer, query = None, name = None):
		if not name: name = "designer-menu-%s" % randomString() # honestly I no longer remember why I require a unique ID for this
		import string
		allowed = string.letters + string.digits + string.punctuation # can't use all due to non-shifting at the moment..
		StringQuery( allowed=allowed, maximalLength=1, inputColour=stdDeviceColour, interface=self.interface, query=query, width=60, onAnswer=onAnswer, name = name )
	def enterText(self, onAnswer, query = None, name = None, allowEmpty = False):
		if not name: name = "designer-menu-%s" % randomString() # honestly I no longer remember why I require a unique ID for this
		StringQuery( interface=self.interface, query=query, width=60, onAnswer=onAnswer, name = name, allowEmpty = allowEmpty)
	def show(self, text, onDismiss):
		name = "designer-menu-%s" % randomString() # honestly I no longer remember why I require a unique ID for this
		TextDisplay( interface=self.interface, text=text, width=60, name=name, onDismiss=onDismiss)
	def callOnAnswer(self, answer):
		answer()

class SettingUnknownPropertyValue: pass
class DeviceDesigner (MenuSystem):
	def __init__(self, game, knownComponents, chassisChoices, prototypeReceiver):
		from erldevice import Device
		MenuSystem.__init__(self, game.interface)
		self.knownComponents = knownComponents
		self.chassisChoices = chassisChoices
		self.device = Device( game )
		self.ourName = None
		self.ourGlyph = None
		self.mainMenu()
		self.prototypeReceiver = prototypeReceiver
	def mainMenu(self):
		options = []
		values = []
		self.device.sensify()
		if not self.device.chassis:
			options.append( "<WARNING>Set chassis type</WARNING>" )
		else:
			options.append( "Set chassis type" )
		values.append( self.setChassis )
		options.append( "Review plans" )
		values.append( self.viewPlans )
		if not self.device.components:
			options.append( "<WARNING>Add component</WARNING>" )
		else:
			options.append( "Add component" )
		values.append( self.addComponent )
		if not self.ourGlyph:
			options.append( "<WARNING>Alter appearance</WARNING>" )
		else:
			options.append( "Alter appearance" )
		values.append( self.alterAppearance )
		if not self.ourName:
			options.append( "<WARNING>Name the device</WARNING>" )
		else:
			options.append( "Name the device" )
		values.append( self.setName )
		options.append( "Label a component" )
		values.append( self.labelComponent )
		options.append( "Remove component" )
		values.append( self.removeComponent )
		if self.device.components and not self.device.sanity():
			options.append( "<WARNING>Set property</WARNING>" )
		else:
			options.append( "Set property" )
		values.append( self.setProperty )
		if not self.chassisOkay():
			options.append( "<WARNING>Show specifications</WARNING>" )
		else:
			options.append( "Show specifications" )
		values.append( self.showSpecs )
		options.append( "Cancel" )
		values.append( self.cancel )
		if self.device.sanity() and self.chassisOkay() and self.ourName and self.ourGlyph:
			options.append( "Finish" )
			values.append( self.finishDesign )
		self.choice( options, values )
	def alterAppearance(self):
		def setGlyph( character ):
			self.ourGlyph = character
			self.device.setGlyphCharacter( character )
			self.mainMenu()
		self.enterGlyph( query="Please enter a character to represent the device.", onAnswer=setGlyph )
	def setName(self):
		def doSetName( newName ):
			self.ourName = newName
			self.device.setName( newName )
			self.mainMenu()
		self.enterText( query = "What do you wish to name your device?", onAnswer=doSetName )
	def setChassis(self):
		def doSetChassis( chassisType ):
			self.device.chassis = chassisType
			self.mainMenu()
		descs = [ chassis.description for chassis in self.chassisChoices ]
		self.choice( descs, self.chassisChoices, query = "What sort of device do you wish to make?", onAnswer=doSetChassis )
	def addComponent(self):
		def chooseComponent(component):
			if component:
				self.device.addComponent( component() )
			self.mainMenu()
		# not a .componentChoice() !
		descs = []
		values = []
		for componentClass in self.knownComponents:
			descs.append( componentClass.componentName )
			values.append( componentClass )
		descs.append( "<DARKBRASS>none</DARKBRASS>" )
		values.append( None )
		self.choice( descs, values, query="Add what sort of component?", onAnswer=chooseComponent )
	def componentChoice(self, query, on):
		descs = []
		values = []
		self.device.sensify()
		for component in self.device.components:
			if not component.sensifiedSanity():
				descs.append( "<WARNING>" + str(component) + "</WARNING>" )
			else:
				descs.append( str(component) )
			values.append( component )
		descs.append( "<DARKBRASS>none</DARKBRASS>" )
		values.append( None )
		self.choice( descs, values, query=query, onAnswer=on )
	def labelComponent(self):
		def chooseComponent(component):
			if component:
				def enteredComponentLabel( newLabel ):
					component.label = newLabel
					self.mainMenu()
				self.enterText( query = "Enter a new label for %s." % str(component), onAnswer = enteredComponentLabel, allowEmpty = True )
			else:
				self.mainMenu()
		self.componentChoice( "Label which component?", chooseComponent )
	def removeComponent(self):
		def chooseComponent(component):
			if component:
				self.device.removeComponent( component )
			self.mainMenu()
		self.componentChoice( "Remove which component?", chooseComponent )
	def cancel(self):
		pass
	def describeComponent(self, component):
		# should show documentation and all variables
		# meant to be displayed on a screen by itself
		text = [ str(component) ]
		text.append( "" )
		text.append( component.componentDescription )
		text.append( "" )
		for property in component.property.keys():
			value, propertyType, propertyDoc = component.property[property]
			if not value:
				value = "<WARNING>not set</WARNING>"
			text.append( "<DARKBRASS>%s</DARKBRASS>: %s" % (property,str(value)) )
			text.append( propertyDoc )
			text.append( "" )
		return "<N>".join( text )
	def viewPlans(self):
		def chooseComponent(component):
			if component:
				text = self.describeComponent(component)
				self.show( text, self.viewPlans )
			else:
				self.mainMenu()
		self.componentChoice( "Inspect which component?", chooseComponent )
	def selectPropertyValue( self, pType, pDoc, do ):
		if pType == "component":
			self.componentChoice( "Which component?", do )
		elif pType == "integer":
			def returnAsInteger(value):
				do( int(value) )
			self.enterInteger( query="Please enter a nonnegative integer.", onAnswer=returnAsInteger )
		elif pType == "boolean":
			def returnAsBoolean(value):
				do( bool(value) )
			self.choice( [ "True", "False" ], [ True, False ], query = "Please choose a boolean value.", onAnswer=returnAsBoolean )
		else:
			raise SettingUnknownPropertyValue()
	def selectPropertyList( self, pType, pDoc, do ):
		class ListKeeper:
			def __init__(subself):
				subself.rvList = []
			def doAddValue(subself, value ):
				if value:
					subself.rvList.append( value )
				subself.main()
			def addValue(subself):
				self.selectPropertyValue( pType, pDoc, subself.doAddValue )
			def doRemoveValue(subself, value):
				if value:
					subself.rvList.remove( value )
				subself.main()
			def removeValue(subself):
				self.choice(
					[ str(elt) for elt in subself.rvList ],
					subself.rvList,
					query = "Remove which value?",
					onAnswer = subself.doRemoveValue,
				)
#				self.selectPropertyValue( pType, pDoc, subself.doRemoveValue )
			def done(subself):
				do( subself.rvList )
			def main(subself):
				listDesc = "<N>".join( [ str(s) for s in subself.rvList ] )
				descs = []
				values = []
				descs.append( "Add a value" )
				values.append( subself.addValue )
				if len( subself.rvList ) > 0:
					descs.append( "Remove a value" )
					values.append( subself.removeValue )
					descs.append( "Done" )
					values.append( subself.done )
				self.choice(
					descs,
					values,
					query = listDesc
				)
		ListKeeper().main()
	def selectProperty(self, propertyItem, do):
		oldValue, propertyType, propertyDoc = propertyItem
		def setHardwiredValue():
			def returnHardcodedValue( value ):
				from erldevice import HardcodedValue
				hv = HardcodedValue( value )
				do( hv )
			self.selectPropertyValue( propertyType, propertyDoc, do = returnHardcodedValue)
		def setCyclicValue():
			def returnCyclicValue( valuelist ):
				if not valuelist:
					do( None )
				from erldevice import CyclicValue
				cv = CyclicValue( valuelist )
				do( cv )
			self.selectPropertyList( propertyType, propertyDoc, do = returnCyclicValue )
		def setRandomValue():
			def returnRandomValue( valuelist ):
				if not valuelist:
					do( None )
				from erldevice import RandomValue
				cv = RandomValue( valuelist )
				do( cv )
			self.selectPropertyList( propertyType, propertyDoc, do = returnRandomValue )
		def setDialValue():
			def returnDialValue( valuelist ):
				if not valuelist:
					do( None )
				from erldevice import DialValue
				cv = DialValue( valuelist )
				do( cv )
			self.selectPropertyList( propertyType, propertyDoc, do = returnDialValue )
		descs = []
		values = []
		descs.append( "Hardwire a single value into the device." )
		values.append( setHardwiredValue )
		descs.append( "Run through a series of values cyclically." )
		values.append( setCyclicValue )
		descs.append( "Run through a series of values randomly." )
		values.append( setRandomValue )
		descs.append( "Link it to a dial on the outside of the device." )
		values.append( setDialValue )
		self.choice( descs, values, query="How do you wish to wire this value?" )
	def setProperty(self):
		def chooseComponent(component):
			def chooseProperty(property):
				def setPropertyDo(value):
					if value:
						component[property] = value
					self.mainMenu()
				if not property:
					self.mainMenu()
					return
				self.selectProperty( component.property[property], do = setPropertyDo )
			if not component:
				self.mainMenu()
				return
			self.device.sensify()
			properties = []
			values = []
			for key in component.property.keys():
				if self.device.propertyOkay( component, key ):
					properties.append( key )
				else:
					properties.append( "<WARNING>%s</WARNING>" % key )
				values.append( key )
			properties.append( "<DARKBRASS>none</DARKBRASS>" )
			values.append( None )
			self.choice( properties, values, query="Modify which property?", onAnswer=chooseProperty )
		self.componentChoice( "Modify which component?", chooseComponent )
	def showSpecs(self):
		from erlitem import Unit
		specs = []
		specs.append(  "%s %s (<YELLOW>%s</YELLOW>)" % ( self.device.name.indefiniteArticle, self.device.name.singular, self.device.glyph.char ) )
		if self.device.chassis:
			specs.append( "<DARKBRASS>%s</DARKBRASS>" % self.device.chassis.shortDescription )
		else:
			specs.append( "<WARNING>no shell chosen</WARNING>" )
		specs.append( "" )
		bulk, weight, resources, materials = self.device.specifications()
		if self.device.chassis and self.device.chassis.bulkLimit and bulk > self.device.chassis.bulkLimit:
			specs.append( "<DARKBRASS>Bulk</DARKBRASS>: <WARNING>%d</WARNING>" % bulk )
		else:
			specs.append( "<DARKBRASS>Bulk</DARKBRASS>: %d" % bulk )
		specs.append( "<DARKBRASS>Resources</DARKBRASS>: %d" % resources )
		if self.device.chassis and self.device.chassis.weightLimit and weight > self.device.chassis.weightLimit:
			specs.append( "<DARKBRASS>Weight</DARKBRASS>: <WARNING>%s</WARNING>" % Unit.weight.describe(weight) )
		else:
			specs.append( "<DARKBRASS>Weight</DARKBRASS>: %s" % Unit.weight.describe(weight) )
		specs.append( "<DARKBRASS>Materials</DARKBRASS>: %s" % Unit.weight.describe(materials) )
		text = "<N>".join( specs )
		self.show( text, self.mainMenu )
	def chassisOkay(self):
		bulk, weight, resources, materials = self.device.specifications()
		if not self.device.chassis:
			return False
		if self.device.chassis and self.device.chassis.bulkLimit < bulk:
			return False
		if self.device.chassis and self.device.chassis.weightLimit < weight:
			return False
		return True
	def finishDesign(self):
		# just the same as "cancel" for now; but really .device
		# should be saved as a "prototype" and become available
		# for building. (probably a .finalize() method or
		# similar should be called to, e.g. figure out costs)
		# make the device prototype (self.device) available!
		# use the .ingredients member to know
		# requirements for creation, and .create( creator )
		# to actually spawn something.
		self.device.finalize()
		self.prototypeReceiver( self.device )
